home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / netprog.zip / NETPROG.TAR / lpr / printbsd.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  11KB  |  413 lines

  1. /*
  2.  * Send files to a Berkeley (BSD) line printer daemon.
  3.  * This is done using a TCP connection, following the protocol
  4.  * inherent (and undocumented) in the BSD printer daemon (/usr/lib/lpd).
  5.  *
  6.  * This program differs from the normal BSD lpr(1) command in the
  7.  * following ways.  The BSD lpr command writes the files to be
  8.  * printed into the spooling directory, and then notifies the
  9.  * line printer daemon (lpd) that the files are there (using a
  10.  * UNIX domain socket message).  The lpd program then sees that
  11.  * the files get printed at some time.  If the files are to be
  12.  * printed on another host's line printer, then the printer daemon
  13.  * will contact the daemon on the other host (using an Internet
  14.  * TCP socket) and will transfer the file to the other daemon,
  15.  * who will then see to it that the file gets printed.
  16.  * This program, however, acts like a UNIX printer daemon (lpd)
  17.  * as we assume the files are to be printed on another system.
  18.  * We go ahead and contact the UNIX lpd program on the remote host
  19.  * (using an Internet TCP socket) and send the files to that
  20.  * daemon for printing.
  21.  *
  22.  * There are three functions in this file that main() calls:
  23.  *    send_start() - called once, before the first file
  24.  *    send_file()  - called for every file that can be opened by main
  25.  *    send_done()  - called at the end
  26.  */
  27.  
  28. #include    "defs.h"
  29. #include    "systype.h"
  30.  
  31. #include    <pwd.h>
  32. #include    <sys/stat.h>
  33.  
  34. /*
  35.  * Variables specific to this file.
  36.  */
  37.  
  38. static FILE    *cfp;            /* file pointer for control file */
  39. static long    cfilesize;        /* size of cfile, in bytes */
  40. static char    myhostname[MAXHOSTNAMELEN]; /* name of host running lpr */
  41. static char    username[MAXHOSTNAMELEN];   /* name of user running lpr */
  42. static char    cfname[MAXFILENAME];    /* name of "cf" file */
  43. static char    dfname[MAXFILENAME];    /* name of "df" file */
  44. static char    buf[MAXLINE];        /* temp buffer */
  45. static int    sockfd;            /* network connection */
  46. static int    seqnum;            /* seq#, set to same value for now */
  47.  
  48. long    get_size();
  49.  
  50. /*
  51.  * Start things up.
  52.  */
  53.  
  54. send_start()
  55. {
  56.     register int    uid;
  57.     struct passwd    *pw, *getpwuid();
  58.  
  59.     DEBUG2("send_start: host = %s, printer = %s", hostname, printername);
  60.  
  61.                         /* we need a reserved port */
  62.     if ( (sockfd = tcp_open(hostname, LPR_SERVICE, -1)) < 0)
  63.         err_quit("can't connect to service: %s on host: %s",
  64.                         LPR_SERVICE, hostname);
  65.  
  66.     /*
  67.      * If we got the reserved port, then we're either running as
  68.      * root, or the program is set-user-ID root.  In the latter case,
  69.      * the only reason we need to be set-user-ID root is to bind the
  70.      * reserved port, so we can now go back to being the "real"
  71.      * user who executed this program.  We really need to do
  72.      * this anyway, to assure we can't read files as root
  73.      * that the user doesn't have normal access to.
  74.      */
  75.  
  76.     setuid(getuid());
  77.  
  78.     /*
  79.      * Get the name of the local host.
  80.      */
  81.  
  82.     if (gethostname(myhostname, MAXHOSTNAMELEN) < 0)
  83.         err_dump("gethostname error");
  84.  
  85.     /*
  86.      * Get the name of the user executing this program.
  87.      */
  88.  
  89.     uid = getuid();
  90.     if ( (pw = getpwuid(uid)) == NULL)
  91.         err_quit("getpwuid failed, uid = %d; who are you", uid);
  92.     strcpy(username, pw->pw_name);
  93.  
  94.     /*
  95.      * We must insert a 3-digit sequence number into the filenames
  96.      * that we're creating to send to the server.  This is to
  97.      * distinguish between successive files that are sent to the
  98.      * same server for the same printer.
  99.      */
  100.  
  101.     seqnum = get_seqno();
  102.  
  103.     sprintf(cfname, "cfA%03d%s", seqnum, myhostname);
  104.     sprintf(dfname, "dfA%03d%s", seqnum, myhostname);
  105.     DEBUG2("cfname = %s, dfname = %s", cfname, dfname);
  106.  
  107.     /*
  108.      * Create the control file and open it for writing.
  109.      */
  110.  
  111.     if ( (cfp = fopen(cfname, "w")) == NULL)
  112.         err_dump("can't open control file: %s for writing", cfname);
  113.     cfilesize = 0L;
  114.  
  115.     /*
  116.      * Initialize the control file by inserting the following lines:
  117.      *
  118.      *    H<hostname>    (host on which the lpr was executed)
  119.      *    P<username>    (person executing the lpr)
  120.      *    J<jobname>    (for the banner page)
  121.      *    C<classname>    (for the banner page)
  122.      *    L<username>    (literal value for banner page)
  123.      *
  124.      * Then, for each file to be printed, send_file() will add
  125.      * the following three lines:
  126.      *
  127.      *    f<df_filename>    (name of text file to print)
  128.      *    U<df_filename>    (to unlink the file after printing)
  129.      *    N<filename>    (real name of the file, used by lpq)
  130.      *
  131.      * The <df_filename> is of the form "df[A-Z]nnn<host>" where
  132.      * "nnn" is the 3-digit sequence number from this host,
  133.      * and "<host>" is the name of the host on which the lpr
  134.      * was executed.
  135.      */
  136.  
  137.     add_H();
  138.     add_P();
  139.     add_C();
  140.     add_L();
  141.  
  142.     /*
  143.      * Send a line to the print server telling it we want
  144.      * to send it some files to print, and specifying the
  145.      * printer to be used.
  146.      */
  147.  
  148.     sprintf(buf, "%c%s\n", '\002', printername);
  149.     if (writen(sockfd, buf, strlen(buf)) != strlen(buf))
  150.         err_dump("writen error");
  151.  
  152.     if (readn(sockfd, buf, 1) != 1)
  153.         err_dump("readn error");
  154.     if (buf[0] != '\0') {
  155.         if (readline(sockfd, &buf[1], MAXLINE-1) > 0)
  156.            err_quit("error, server returned: %s", buf);
  157.         else
  158.            err_dump("didn't get ACK from server, got 0x%02x", buf[0]);
  159.     }
  160. }
  161.  
  162. /*
  163.  * Send a single file.
  164.  * This function is called by main once for every file to be printed.
  165.  * Main has already opened the file for reading, but it still passes
  166.  * us the actual filename from the command line, so that we can
  167.  * use the filename for identifying the file to the server.
  168.  */
  169.  
  170. send_file(filename, fp)
  171. char    *filename;    /* filename from command line, or "-stdin" */
  172. FILE    *fp;        /* file pointer on which file is open for reading */
  173. {
  174.     static int    filecount = 0;
  175.     register char    *ptr;
  176.     char        *rindex();
  177.  
  178.     /*
  179.      * We don't currently handle standard input.  To do so requires
  180.      * that we copy stdin to a temporary file, so that we can get
  181.      * its size in bytes.  We have to know the file's size before
  182.      * we send it to the server.
  183.      */
  184.  
  185.     if (strcmp(filename, "-stdin") == 0) {
  186.         err_ret("can't currently print standard input");
  187.         return;
  188.     }
  189.  
  190.     filecount++;
  191.  
  192.     /*
  193.      * First strip any leading directory names off the filename.
  194.      * This is to get the base filename for the job banner.
  195.      */
  196.  
  197.     if ( (ptr = rindex(filename, '/')) != NULL) {
  198.         filename = ptr + 1;
  199.     }
  200.  
  201.     /*
  202.      * If this is the first file, set the Job Class on the banner
  203.      * page to the filename.
  204.      */
  205.  
  206.     if (filecount == 1)
  207.         add_J(filename);
  208.     else
  209.         dfname[2]++;    /* A, B, C, ... */
  210.  
  211.     /*
  212.      * Add the 'f', 'U' and 'N' lines to the control file.
  213.      */
  214.  
  215.     add_f(dfname);
  216.     add_U(dfname);
  217.     add_N(filename);
  218.  
  219.     DEBUG2("send_file: %s, dfname = %s", filename, dfname);
  220.  
  221.     xmit_file(filename, fp, dfname, '\003');
  222.                 /* transmit file across network */
  223. }
  224.  
  225. /*
  226.  * All done with the user's files.
  227.  * Now we must transmit the control file that we've been building
  228.  * to the other side.
  229.  */
  230.  
  231. send_done()
  232. {
  233.     fclose(cfp);
  234.  
  235.     if ( (cfp = fopen(cfname, "r")) == NULL)
  236.         err_dump("can't reopen cfile for reading");
  237.  
  238.     xmit_file("-cfile", cfp, cfname, '\002');
  239.  
  240.     fclose(cfp);
  241.  
  242.     /*
  243.      * We're done with the control file, so delete it.
  244.      * (Don't unlink if debugflag is 1, assuming we're debugging.)
  245.      */
  246.  
  247.     if (debugflag == 0 && unlink(cfname) < 0)
  248.         err_dump("can't unlink control file: %s", cfname);
  249.  
  250.     close(sockfd);
  251. }
  252.  
  253. /*
  254.  * Transmit one file to the server.
  255.  * This routine is used to send both the actual text files (data files)
  256.  * that the user wants printed, and to send the control file (cfile)
  257.  * that we build up as we send the data files.
  258.  * The only difference between transmitting these two types of files
  259.  * is the first byte of the transmission (002 for the cfile and 003
  260.  * for the dfiles).
  261.  */
  262.  
  263. xmit_file(filename, fp, fname, xmittype)
  264. char    *filename;    /* name from command line, or "-stdin" or "-cfile" */
  265. FILE    *fp;
  266. char    *fname;        /* the cfname or dfname */
  267. char    xmittype;    /* '\002' or '\003' */
  268. {
  269.     register long    size;
  270.  
  271.     /*
  272.      * We have to get the exact size of the file in bytes
  273.      * to send to the server, so that it knows how much
  274.      * data to read from the net.
  275.      */
  276.  
  277.     size = get_size(filename, fp);
  278.     DEBUG2("xmit_file: %s, size = %ld", filename, size);
  279.  
  280.     /*
  281.      * Send a line to the print server giving the type of
  282.      * file, the exact size of the file in bytes,
  283.      * and the name of the file (its dfname, not its actual
  284.      * name).
  285.      */
  286.  
  287.     sprintf(buf, "%c%ld %s\n", xmittype, size, fname);
  288.     if (writen(sockfd, buf, strlen(buf)) != strlen(buf))
  289.         err_dump("writen error");
  290.     if (readn(sockfd, buf, 1) != 1)
  291.         err_dump("readn error");
  292.     if (buf[0] != '\0')
  293.         err_dump("didn't get an ACK from server, got 0x%02x", buf[0]);
  294.  
  295.     /*
  296.      * Now send the actual file itself.
  297.      */
  298.  
  299.     copyfile(fp);
  300.  
  301.     /*
  302.      * Write a byte of zero to the server, and wait for
  303.      * a byte of zero to be returned from the server,
  304.      * telling us all is OK (I'm OK, you're OK).
  305.      */
  306.  
  307.     if (writen(sockfd, "", 1) != 1)
  308.         err_dump("writen error");
  309.     if (readn(sockfd, buf, 1) != 1)
  310.         err_dump("readn error");
  311.     if (buf[0] != '\0')
  312.         err_dump("didn't get an ACK from server, got 0x%02x", buf[0]);
  313. }
  314.  
  315. /*
  316.  * Copy a file to the network.
  317.  * We read the file using standard i/o, one line at a time,
  318.  * and write the data to the network one line at a time.
  319.  */
  320.  
  321. copyfile(fp)
  322. FILE    *fp;
  323. {
  324.     register int    len;
  325.     char        line[MAXLINE];
  326.  
  327.     while (fgets(line, MAXLINE, fp) != NULL) {
  328.         len = strlen(line);
  329.         if (writen(sockfd, line, len) != len)
  330.             err_dump("writen error");
  331.     }
  332.  
  333.     if (ferror(fp))
  334.         err_dump("read error from fgets");
  335. }
  336.  
  337. /*
  338.  * Determine the exact size of a file.
  339.  * Under UNIX this is easy - we just call the fstat() system call.
  340.  * Under other operating systems it is harder, since they may not use
  341.  * exactly one character to represent a newline.
  342.  */
  343.  
  344. long
  345. get_size(filename, fp)
  346. char    *filename;
  347. FILE    *fp;
  348. {
  349.     struct stat    statbuff;
  350.  
  351.     if (fstat(fileno(fp), &statbuff) < 0)
  352.         err_dump("can't fstat");
  353.  
  354.     return(statbuff.st_size);
  355. }
  356.  
  357. add_H()
  358. {
  359.     fprintf(cfp, "H%s\n", myhostname);
  360.     cfilesize += strlen(myhostname) + 2;
  361. }
  362.  
  363. add_P()
  364. {
  365.     fprintf(cfp, "P%s\n", username);
  366.     cfilesize += strlen(username) + 2;
  367. }
  368.  
  369. /*
  370.  * We add the Job Class when the first file is processed.
  371.  */
  372.  
  373. add_J(filename)
  374. char    *filename;
  375. {
  376.     fprintf(cfp, "J%s\n", filename);
  377.     cfilesize += strlen(filename) + 2;
  378. }
  379.  
  380. add_C()
  381. {
  382.     fprintf(cfp, "C%s\n", myhostname);
  383.     cfilesize += strlen(myhostname) + 2;
  384.                 /* just use this host's name */
  385. }
  386.  
  387. add_L()
  388. {
  389.     fprintf(cfp, "L%s\n", username);
  390.     cfilesize += strlen(username) + 2;
  391. }
  392.  
  393. add_f(dfname)
  394. char    *dfname;
  395. {
  396.     fprintf(cfp, "f%s\n", dfname);
  397.     cfilesize += strlen(dfname) + 2;
  398. }
  399.  
  400. add_U(dfname)
  401. char    *dfname;
  402. {
  403.     fprintf(cfp, "U%s\n", dfname);
  404.     cfilesize += strlen(dfname) + 2;
  405. }
  406.  
  407. add_N(filename)
  408. char    *filename;
  409. {
  410.     fprintf(cfp, "N%s\n", filename);
  411.     cfilesize += strlen(filename) + 2;
  412. }
  413.